#include "polygon.h"

//-------------------------------------------------------------------------------------------
Polygon::Polygon()
{
	this->vertexes.clear();
	this->bounding = QRectF(0,0,0,0);
}

//-------------------------------------------------------------------------------------------
Polygon::~Polygon()
{
}

//-------------------------------------------------------------------------------------------
float Polygon::triangleArea(QPointF v1, QPointF v2, QPointF v3)
{
	QPointF p1;
	QPointF p2;
	p1.setX(v2.x() - v3.x());
	p1.setY(v2.y() - v3.y());
	p2.setX(v1.x() - v3.x());
	p2.setY(v1.y() - v3.y());
	return (p1.x() * p2.y() - p1.y() * p2.x()) / 2;
}

//-------------------------------------------------------------------------------------------
int Polygon::side(QPointF p1, QPointF p2, QPointF p3)
{
	QPointF vec1, vec2;
	vec1 = p2 - p1;
	vec2 = p3 - p1;
	int l = (int) (vec1.x() * vec2.y() - vec1.y() * vec2.x());
	if (l > 0) return 1;
	if (l < 0) return -1;
	return 0;
}

//-------------------------------------------------------------------------------------------
void Polygon::update()
{
	if (!this->vertexes.empty()){
		QPointF max, min;
		max = this->vertexes.first();
		min = this->vertexes.first();
		for (int i = 1; i < this->vertexes.size(); i++){
			if (this->vertexes.at(i).x() > max.x()) max.setX(this->vertexes.at(i).x());
			if (this->vertexes.at(i).y() > max.y()) max.setY(this->vertexes.at(i).y());
			if (this->vertexes.at(i).x() < min.x()) min.setX(this->vertexes.at(i).x());
			if (this->vertexes.at(i).y() < min.y()) min.setY(this->vertexes.at(i).y());
		}
		this->bounding = QRectF(min, max);
	}else
		this->bounding = QRectF(0,0,0,0);
}

//-------------------------------------------------------------------------------------------
QList<QPointF> Polygon::getVertexes()
{
	return this->vertexes;
}

//-------------------------------------------------------------------------------------------
QRectF Polygon::boundingBox()
{
	return this->bounding;
}

//-------------------------------------------------------------------------------------------
QPointF Polygon::getVertexAt(int index)
{
	return this->vertexes.at(index);
}

//-------------------------------------------------------------------------------------------
QPointF Polygon::center()
{
	return this->bounding.center();
}

//-------------------------------------------------------------------------------------------
QPointF Polygon::position()
{
	return this->bounding.topLeft();
}

//-------------------------------------------------------------------------------------------
Polygon Polygon::rotate(float angle)
{
	QPointF point;
	QPointF result;
	QList<QPointF> points;
	Polygon polygon;
	for (int i = 0; i < this->vertexes.size(); i++){
		point = this->vertexes.at(i) - this->center();
		float radian =((3.141592f * angle) / 180.00f);
		float x = cosf(radian) * point.x() - sinf(radian) * point.y() + this->center().x();
		float y = sinf(radian) * point.x() + cosf(radian) * point.y() + this->center().y();
		result.setX(qRound(x));
		result.setY(qRound(y));
		points += result;
	}
	polygon.addVertexes(points);
	return polygon;
}

//-------------------------------------------------------------------------------------------
Polygon Polygon::scale(int porcentage)
{
	QPointF point;
	QPointF result;
	Polygon polygon;
	QList<QPointF> points;
	for (int i = 0; i < this->vertexes.size(); i++){
		point = this->vertexes.at(i) - this->position();
		float coefficient = porcentage / 100.00f;
		result.setX(qRound(point.x() * coefficient + this->position().x()));
		result.setY(qRound(point.y() * coefficient + this->position().y()));
		points += result;
	}
	polygon.addVertexes(points);
	return polygon;
}

//-------------------------------------------------------------------------------------------
Polygon Polygon::translate(QPointF point)
{
	Polygon polygon;
	QList<QPointF> points;
	QPointF translate_vector = this->position() - point;
	for (int i = 0; i < this->vertexes.size(); i++){
		QPointF vertex = this->vertexes.at(i) - translate_vector;
		points += vertex;
	}
	polygon.addVertexes(points);
	return polygon;
}

//-------------------------------------------------------------------------------------------
void Polygon::addVertex(QPointF vertex)
{
	this->vertexes += vertex;
	this->update();
}

//-------------------------------------------------------------------------------------------
void Polygon::addVertexes(QList<QPointF> vertexes)
{
	this->vertexes += vertexes;
	this->update();
}

//-------------------------------------------------------------------------------------------
void Polygon::insertVertexAt(int index, QPointF vertex)
{
	this->vertexes.insert(index, vertex);
	this->update();
}

//-------------------------------------------------------------------------------------------
void Polygon::removeVertex(QPointF vertex)
{
	bool find = false;
	for (int i = 0; i < this->vertexes.size() && !find; i++){
		if (this->vertexes.at(i) == vertex){
			find = true;
			this->vertexes.removeAt(i);
		}
	}
	this->update();
}

//-------------------------------------------------------------------------------------------
void Polygon::removeVertexAt(int index)
{
	this->vertexes.removeAt(index);
	this->update();
}

//-------------------------------------------------------------------------------------------
void Polygon::removeVertexes()
{
	this->vertexes.clear();
	this->update();
}

//-------------------------------------------------------------------------------------------
void Polygon::moveVertex(int index, QPointF point)
{
	if (index < this->vertexes.size()){
		this->vertexes.replace(index, point);
		this->update();
	}
}

//-------------------------------------------------------------------------------------------
void Polygon::rotateMe(float angle)
{
	QPointF point;
	QPointF result;
	QList<QPointF> points;
	for (int i = 0; i < this->vertexes.size(); i++){
		point = this->vertexes.at(i) - this->center();
		float radian =((3.141592f * angle) / 180.00f);
		float x = cosf(radian) * point.x() - sinf(radian) * point.y() + this->center().x();
		float y = sinf(radian) * point.x() + cosf(radian) * point.y() + this->center().y();
		result.setX(qRound(x));
		result.setY(qRound(y));
		points += result;	
	}
	this->vertexes.clear();
	this->addVertexes(points);
}

//-------------------------------------------------------------------------------------------
void Polygon::scaleMe(int porcentage)
{
	QPointF point;
	QPointF result;
	QList<QPointF> points;
	
	float coefficient = porcentage / 100.00f;
	for (int i = 0; i < this->vertexes.size(); i++){
		point = this->vertexes.at(i) - this->position();
		point.setX(qRound(point.x() * coefficient));
		point.setY(qRound(point.y() * coefficient));
		result = point + this->position();
		points += result;
	}
	
	this->vertexes.clear();
	this->addVertexes(points);
}

//-------------------------------------------------------------------------------------------
void Polygon::translateMe(QPointF point)
{
	QList<QPointF> points;
	QPointF translate_vector = point - this->position(); 
	QPointF vertex;
	for (int i = 0; i < this->vertexes.size(); i++){
		QPointF vertex = this->vertexes.at(i) + translate_vector;
		points += vertex;
	}
	this->vertexes.clear();
	this->addVertexes(points);
}

//-------------------------------------------------------------------------------------------
void Polygon::reflectMe(int type)
{
	QPointF result;
	QList<QPointF> points;
	for (int i = 0; i < this->vertexes.size(); i++){
		QPointF point = this->vertexes.at(i) - this->center();
		if (type == Vertical || type == VerticalHorizontal)
			result.setX(qRound(point.x() * -1));
		else
			result.setX(qRound(point.x()));
		if (type == Horizontal || type == VerticalHorizontal)
			result.setY(qRound(point.y() * -1));
		else
			result.setY(qRound(point.y()));
		result += this->center();
		points += result;	
	}
	this->vertexes.clear();
	this->addVertexes(points);
}

//-------------------------------------------------------------------------------------------
float Polygon::height()
{
	return this->bounding.height();
}

//-------------------------------------------------------------------------------------------
float Polygon::width()
{
	return this->bounding.width();
}

//-------------------------------------------------------------------------------------------
float Polygon::area()
{
	float area = 0;
	QPointF lastPoint = this->vertexes.first();
	for (int i = 1; i < this->vertexes.size();i++){
		QPointF point = this->vertexes.at(i);
		area += this->triangleArea(QPointF(0,0), lastPoint, point);
		lastPoint = point;
	}
	area += this->triangleArea(QPointF(0,0), lastPoint, this->vertexes.first());
	if (area < 0)
		return -area;
	else 
		return area;
}

//-------------------------------------------------------------------------------------------
bool Polygon::inside(QPointF point)
{
	QPointF origin = point;
	QPointF destiny;
	int side = 0;
	int sideA, cuts = 0, i;
	destiny.setX(this->bounding.bottomRight().x() + 999);
	destiny.setY(origin.y());
	
	for (i = 0; (i < this->vertexes.size()) && (this->vertexes.at(i).x() < origin.x()); i++);
	if (i == this->vertexes.size()){
		return false;
	}
	else{
		for (int j = i; j < this->vertexes.size() && this->vertexes.at(j).x() >= origin.x(); j++){
			sideA = this->side(origin, destiny, this->vertexes.at(j));
			if (side == 0)
				side = sideA;
			else
				if (sideA != 0)
					if (side != sideA){
						cuts++;
						side = sideA;
					}
		}
	}
	cuts = cuts % 2;
	return (bool)cuts;
}

//-------------------------------------------------------------------------------------------
bool Polygon::collision(Polygon polygon)
{
	// VERIFICAR SI FUNCIONA... (TEST DE COLISION DE BOUNDINGBOX)
	return this->bounding.intersects(polygon.boundingBox());
}

//-------------------------------------------------------------------------------------------
bool Polygon::operator <(Polygon polygon)
{
	return (this->area() < polygon.area());
}

//-------------------------------------------------------------------------------------------
bool Polygon::operator >(Polygon polygon)
{
	return (this->area() > polygon.area());
}

//-------------------------------------------------------------------------------------------
bool Polygon::operator ==(Polygon polygon)
{
	bool equals = true;
	for (int i = 0; i < this->vertexes.size() && equals; i++)
		if (this->vertexes.at(i) != polygon.getVertexAt(i)) 
			equals = false;
	return equals;
}

//-------------------------------------------------------------------------------------------
bool Polygon::isEmpty()
{
	return this->vertexes.isEmpty();
}

//-------------------------------------------------------------------------------------------
int Polygon::count()
{
	return this->vertexes.size();
}
